<?php


namespace Xiaoguai\DesignPattern\Main;

use Xiaoguai\DesignPattern\ {
    DP002\Factory\Factory\HuaweiFactory,
    DP002\Factory\Factory\XiaomiFactory,
    DP003\Decorator\LogDecorator,
    DP003\Decorator\PrefixDecorator,
    DP004\Adapter\RedisAdapter,
    DP005\Observer\Observer,
    DP005\Subject\Subject,
    DP006\IteratorInterface,
    DP006\MyAggregate,
    DP007\RedisCacheCloneable,
    DP008\Command\Command,
    DP008\Invoker,
    DP009\Context,
    DP009\Strategy\Strategy,
    DP010\Handler\Handler1,
    DP010\Handler\Handler2,
    DP010\Handler\Handler3,
    DP011\Subject\ProxySubject,
    DP012\FlyweightFactory,
    DP012\UnsharedFlyweight,
    DP013\Composite\Composite,
    DP013\Leaf\Leaf,
    DP014\Colleague\Colleague1,
    DP014\Colleague\Colleague2,
    DP014\Mediator\Mediator,
    DP015\Builder\Builder,
    DP015\Director,
    DP016\Caretaker,
    DP016\Memento,
    DP016\Originator,
    DP017\Abstraction\RefinedAbstraction,
    DP017\Impelementor\ImplementorA,
    DP017\Impelementor\ImplementorB,
    DP018\Facade,
    DP019\Concrete\SubClassA,
    DP019\Concrete\SubClassB,
    DP020\Singleton,
    DP021\Context as StateContext,
    DP021\State\StateA,
    DP022\Element\ElementA,
    DP022\Element\ElementB,
    DP022\Structure,
    DP022\Visitor\Visitor,
    DP023\Context as BusContext,
    Main\Base\CacheDriver\RedisCache,
    Main\Base\CacheInterface,
    DP000\CacheFactory,
    DP001\CacheFactory\RedisCacheFactory,
    DP001\CacheFactory\FileCacheFactory,
    DP005\Temp\Subject as MySplSubject,
    DP005\Temp\Observer as MySplObserver,
    DP006\Temp\RedisCache as SplRedisCache,
};

class MainTest
{
    /**
     * 验证创建的类
     * @param $redis_cache CacheInterface
     * @param $file_cache CacheInterface
     */
    protected function test_cache_class($redis_cache, $file_cache)
    {
        // assert a redis cache class
        assert($redis_cache->getName() == 'redis', 'factory create cache not redis');

        // assert a file cache class
        assert($file_cache->getName() == 'file', 'factory create cache not file');

        // assert cache implements BaseCacheInterface
        assert($redis_cache instanceof CacheInterface, 'redis cache not match interface');
        assert($file_cache instanceof CacheInterface, 'file cache not match interface');

        // assert cache has get method to get cache
        assert(method_exists($redis_cache, 'get'), 'method has no method: get');
        assert(method_exists($file_cache, 'get'), 'method has no method: get');
    }

    /**
     * 简单工厂模式
     * 屏蔽创造类的细节,由工厂来负责如何创建类,只返回结果给客户端调用
     */
    public function dp_000()
    {
        // create a redis cache class
        $redis_cache = CacheFactory::createCache('redis');

        // create a file cache class
        $file_cache = CacheFactory::createCache('file');

        // validate
        $this->test_cache_class($redis_cache, $file_cache);
    }


    /**
     * 工厂方法模式
     * 不再是单一的工厂,将工厂的权利下放,由具体的工厂通过实现接口来创建不同类
     */
    public function dp_001()
    {
        // create redis cache class
        $redis_cache = RedisCacheFactory::createCache();

        // create file cache class
        $file_cache = FileCacheFactory::createCache();

        // validate
        $this->test_cache_class($redis_cache, $file_cache);
    }

    /**
     * 抽象工厂模式
     * 不再以产品维度去管理类创建,而是以工厂为维度,工厂根据自己特性生产不同的产品族
     * 通过切换不同的工厂,来实现不同的产品族
     */
    public function dp_002()
    {
        // get factory xiaomi
        $factory_xiaomi = new XiaomiFactory();
        $computer = $factory_xiaomi->createComputer();
        $phone = $factory_xiaomi->createPhone();

        // get factory huawei
        $factory_huawei = new HuaweiFactory();
        $computer = $factory_huawei->createComputer();
        $phone = $factory_huawei->createPhone();
    }

    /**
     * 装饰器模式
     * 装饰器抽象类实现了原类的接口,通过对原类进行代理调用,由装饰器子类增加或修改原类的功能
     */
    public function dp_003()
    {
        // 创建原始对象
        $cache = new RedisCache();

        // 装饰
        $decorator_cache = new LogDecorator($cache);
        $decorator_cache->get('name');

        // 增加key
        $decorator_cache = new PrefixDecorator($decorator_cache);
        $decorator_cache->get('name');
    }

    /**
     * 适配器模式
     * 适配器类通过继承原类,和实现目标接口,对原类进行对目标的适配
     */
    public function dp_004()
    {
        // create adapter class
        $cache = new RedisAdapter();

        // except json result
        $cache->get('json_key');
    }

    /**
     * 观察者模式
     * 原类通过继承主题抽象类的观察者添加删除的方法,当状态改变时,通知观察者更新
     */
    public function dp_005()
    {
        // new subject
        $subject = new Subject();

        // add 2 observer
        $observer1 = new Observer();
        $subject->attach($observer1);

        $observer2 = new Observer();
        $subject->attach($observer2);

        // do something
        $subject->setState(PHP_OS);

        // remove observer2
        $subject->detach($observer2);

        // do something again
        $subject->setState(PHP_SAPI);


        // 利用PHP官方spl库
        $subject = new MySplSubject();

        $observer1 = new MySplObserver();
        $observer2 = new MySplObserver();
        $subject->attach($observer1);
        $subject->attach($observer2);

        $subject->setState(PHP_OS_FAMILY);
    }


    /**
     * 迭代器模式
     * 分离聚合与迭代,实现聚合器接口来添加删除项目;迭代时,返回实现迭代器接口的迭代对象
     */
    public function dp_006()
    {
        // 聚合
        $aggregate = new MyAggregate();
        $aggregate->add('hello');
        $aggregate->add('world');
        $aggregate->add('php');

        // 遍历
        $iterator = $aggregate->getIterator();
        while ($iterator->hasNext()) {
            echo $iterator->next();
        }

        // 利用PHP官方库
        $aggregate = new SplRedisCache();
        
        $aggregate->add('hello');
        $aggregate->add('world');
        
        $iterator = $aggregate->getIterator();
        foreach ($iterator as $item) {
            echo $item;
        }
    }

    /**
     * 原型模式
     * 通过实现clone方法,来对对象进行深拷贝,复制类时不再重新创建而是直接clone
     */
    public function dp_007()
    {
        $redis = new RedisCacheCloneable();

        $redis2 = clone $redis;

        echo $redis == $redis2 ? 'true' : 'false';
    }

    /**
     * 命令模式
     * 将一个请求封装为一个对象，使发出请求的责任和执行请求的责任分割开。
     * 这样两者之间通过命令对象进行沟通，这样方便将命令对象进行储存、传递、调用、增加与管理。
     * Command命令规定好具体的Receiver执行任务, 由Invoker根据不同的命令发出请求
     */
    public function dp_008()
    {
        // pick a command
        $command = new Command();

        // put a command to invoker
        $invoker = new Invoker($command);
        // call
        $invoker->call();

        // pick a command and call
        $command2 = new Command();
        $invoker->setCommand($command2);
        $invoker->call();
    }

    /**
     * 策略模式
     * strategy实现具体的方法,由Context进行代理调用执行
     * 该模式定义了一系列算法，并将每个算法封装起来，使它们可以相互替换，且算法的变化不会影响使用算法的客户
     */
    public function dp_009()
    {
        // prepare a strategy
        $strategy = new Strategy();

        // create a context
        $context = new Context($strategy);
        $context->action();

        // invoke a another strategy
        $strategy2 = new Strategy();
        $context->setStrategy($strategy2);
        $context->action();
    }

    /**
     * 责任链模式
     * 通过将不同等级的处理着组装成责任处理链条,由基层处理着接受请求,责任自动传递
     * 为了避免请求发送者与多个请求处理者耦合在一起
     */
    public function dp_010()
    {
        // 组装责任链
        $handler1 = new Handler1();
        $handler2 = new Handler2();
        $handler3 = new Handler3();

        $handler1->setNext($handler2);
        $handler2->setNext($handler3);

        // 提交请求
        $handler1->handleRequest('handler3');
    }

    /**
     * 代理模式
     * 隐藏类的某部分,代理控制客户端间接地访问对象
     */
    public function dp_011()
    {
        // 创建对象代理
        $proxy = new ProxySubject();

        // 执行代理的请求
        $proxy->request();
    }

    /**
     * 享元模式
     */
    public function dp_012()
    {
        // 创建享元工厂
        $factory = new FlyweightFactory();

        // 共享对象
        $flyweight1 = $factory->getFlyweight('A');
        $flyweight2 = $factory->getFlyweight('A');

        // 复用对象,传入各自参数,执行相同的方法
        $flyweight1->operation(new UnsharedFlyweight($info = ['extra_info_1']));
        $flyweight2->operation(new UnsharedFlyweight($info = ['extra_info_2']));

        // 创建另一个对象
        $factory->getFlyweight('B');
    }

    /**
     * 组合模式
     */
    public function dp_013()
    {
        // 创建结点
        $root = new Composite();

        $c1 = new Composite();
        $c2 = new Composite();
        $c3 = new Composite();

        // 创建叶子
        $leaf1 = new Leaf('a1');
        $leaf2 = new Leaf('a2');
        $leaf3 = new Leaf('a2');

        // 组装
        $root->add($c1);
        $root->add($c2);
        $c1->add($c3);

        $c1->add($leaf1);
        $c1->add($leaf2);
        $c2->add($leaf3);

        // 访问调用
        $root->operation();
    }

    /**
     * 中介者模式
     */
    public function dp_014()
    {
        // 创建中介
        $mediator = new Mediator();

        // 创建同事
        $colleague1 = new Colleague1();
        $colleague2 = new Colleague2();

        // 注册
        $mediator->register($colleague1);
        $mediator->register($colleague2);

        // 通信
        $colleague1->send();
        $colleague2->send();
    }

    /**
     * 建造者模式
     */
    public function dp_015()
    {
        // 创建构造器
        $builder = new Builder();

        // 创建指挥者
        $director = new Director($builder);

        // $product = (new Director(new Builder()))->construct();

        // 构造
        $product = $director->construct();

        // 展示
        $product->show();
    }

    /**
     * 备忘录模式
     */
    public function dp_016()
    {
        // 创建发起人
        $originator = new Originator();
        $originator->setState('状态1');

        // 基于当前状态创建备忘录
        $memento = $originator->createMemento();

        // 将备忘录放入管理员中管理
        $caretaker = new Caretaker();
        $caretaker->setMemento($memento);

        // 改变状态
        $originator->setState('状态2');

        // 从管理员中获取备份并恢复状态
        $originator->restoreMemento($caretaker->getMemento());
    }

    /**
     * 桥接模式
     * 抽象部分与实现部分分离,解耦对象的依赖,可以替换实现
     * 解耦对象的依赖: 比如: 站内信/email/sms + info/warning/danger <=>  A*B => A+B
     */
    public function dp_017()
    {
        // 具体实现者A (实现部分)
        $implementorA = new ImplementorA();

        // 桥接调用 (抽象部分)
        $refinedAbstraction = new RefinedAbstraction($implementorA);
        $refinedAbstraction->operation();

        // 另一个桥接调用
        $refinedAbstraction->setImplementor(new ImplementorB());
        $refinedAbstraction->operation();
    }

    /**
     * 门面模式
     * 单一职责原则
     */
    public function dp_018()
    {
        // 创建facade
        $facade = new Facade();

        // 有facade来代理对子系统的调用
        $facade->operation();
    }

    /**
     * 模板方法模式
     */
    public function dp_019()
    {
        // 创建sub_class
        $sub_class1 = new SubClassA();

        // 调用模板方法
        $sub_class1->templateMethod();

        // 创建sub_class2并调用模板方法
        $sub_class2 = new SubClassB();
        $sub_class2->templateMethod();
    }

    /**
     * 单例模式
     */
    public function dp_020()
    {
        $instance = Singleton::getInstance();

        $instance2 = Singleton::getInstance();

        var_dump($instance === $instance2);
    }

    /**
     * 状态模式
     */
    public function dp_021()
    {
        // 创建state
        $stateA = new StateA();
        $context = new StateContext($stateA);

        // 自动切换state并handle
        $context->handle();
        $context->handle();
        $context->handle();
        $context->handle();
    }

    /**
     * 访问者模式
     */
    public function dp_022()
    {
        $structure = new Structure();
        $structure->addElement(new ElementA());
        $structure->addElement(new ElementB());

        // 调用visitor对structure改造
        $structure->accept(new Visitor());
    }

    /**
     * 解释器模式
     */
    public function dp_023()
    {
        // 创建context
        $bus = new BusContext();

        // 开始传参计算
        $bus->freeRide('韶关的老人');

        $bus->freeRide('深圳的儿童');
    }
}